package me.xiaoyan.android.net;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONObject;

import android.accounts.NetworkErrorException;
import android.annotation.SuppressLint;

public class HttpManager {

	private static final String SCHEME_HTTPS = "https";
	private static final String SCHEME_HTTP = "http";

	private static final int CONNECTION_TIMEOUT = 5 * 1000;
	private static final int SOCKET_TIMEOUT = 15 * 1000;

	public enum HTTPMETHOD {
		POST, GET
	}

	/**
	 * 
	 * @param url
	 *            服务器地址
	 * @param method
	 *            "GET" or "POST"
	 * @param params
	 *            参数
	 * @param headers
	 *            发送头
	 * @return 服务器 返回对象
	 * @throws IOException
	 * @throws NetworkErrorException
	 */
	public static HttpDatas openUrl(String url, HTTPMETHOD method,
			WebParameters params, List<RequestHeader> headers)
			throws IOException, NetworkErrorException {

		HttpClient client = getHttpClient();
		HttpUriRequest request = null;
		ByteArrayOutputStream bos = null;
		if (method == HTTPMETHOD.POST) {
			HttpPost post = new HttpPost(url);
			request = post;
			bos = new ByteArrayOutputStream();
			byte[] data = null;
			String contentType = params == null ? null : params
					.getValue("content-type");

			if (contentType != null) {
				params.remove("content-type");
				post.setHeader("Content-Type", contentType);
			} else {
				post.setHeader("Content-Type",
						"application/x-www-form-urlencoded");
			}

			String postParam = params == null ? "" : params.toString();
			data = postParam.getBytes("UTF-8");
			bos.write(data);
			data = bos.toByteArray();
			bos.close();
			ByteArrayEntity formEntity = new ByteArrayEntity(data);
			post.setEntity(formEntity);
		} else {
			url = bulildUrl(url, params);
			request = new HttpGet(url);
		}
		if (headers != null) {
			for (RequestHeader requestHeader : headers) {
				request.addHeader(requestHeader.getName(),
						requestHeader.getValue());
			}
		}
		return getHttpData(client, request);
	}

	/**
	 * 用于将json发送到服务器，只能post
	 * 
	 * @param url
	 *            服务器地址
	 * @param method
	 *            "GET" or "POST"
	 * @param params
	 *            参数
	 * @param headers
	 *            发送头
	 * @return 服务器 返回对象
	 * @throws IOException
	 * @throws NetworkErrorException
	 */
	public static HttpDatas openUrl(String url, Map<String, Object> param,
			List<RequestHeader> headers) throws IOException,
			NetworkErrorException {
		String postParam = new JSONObject(param).toString();
		return openUrl(url, postParam, headers);
	}

	public static HttpDatas openUrl(String url, String postData,
			List<RequestHeader> headers) throws IOException,
			NetworkErrorException {
		HttpClient client = getHttpClient();
		HttpUriRequest request = null;
		ByteArrayOutputStream bos = null;
		HttpPost post = new HttpPost(url);
		request = post;
		bos = new ByteArrayOutputStream();
		byte[] data = postData.getBytes("UTF-8");
		bos.write(data);
		data = bos.toByteArray();
		bos.close();
		ByteArrayEntity formEntity = new ByteArrayEntity(data);
		post.setEntity(formEntity);
		if (headers != null) {
			// 设置头
			for (RequestHeader requestHeader : headers) {
				request.addHeader(requestHeader.getName(),
						requestHeader.getValue());
			}
		}
		return getHttpData(client, request);
	}

	/**
	 * 
	 * @param url
	 *            服务器地址
	 * @param method
	 *            "GET" or "POST"
	 * @param postData
	 *            发送数据
	 * @return 服务器 返回对象
	 * @throws IOException
	 * @throws NetworkErrorException
	 */
	public static HttpDatas openUrl(String url, HTTPMETHOD method,
			String postData) throws IOException, NetworkErrorException {
		HttpClient client = getHttpClient();
		HttpPost request = new HttpPost(url);
		request.setHeader("Content-Type", "application/x-www-form-urlencoded");
		ByteArrayEntity formEntity = new ByteArrayEntity(
				postData.getBytes("UTF-8"));
		request.setEntity(formEntity);

		return getHttpData(client, request);
	}

	private static HttpDatas getHttpData(HttpClient client,
			HttpUriRequest request) throws ClientProtocolException,
			IOException, NetworkErrorException {
		HttpResponse response = null;
		try {
			response = client.execute(request);
		} catch (IOException e) {
			/*
			 * 有时会报java.io.IOException: SSL shutdown failed,所以这里进行第二次发送
			 */
			response = client.execute(request);
		} catch (NullPointerException e) {
			/*
			 * ctwap联网方式中，长时间没有联过网络之后的第一次
			 * 以post方式联网，会报NullPointerException,所以这里进行第二次发送
			 */
			response = client.execute(request);
		}
		StatusLine status = response.getStatusLine();

		HttpDatas data = new HttpDatas();
		Header[] headers = response.getAllHeaders();
		for (Header header : headers) {
			data.setHeaders(header.getName(), header.getValue());
		}
		int statusCode = status.getStatusCode();
		if (statusCode != 200) {
			throw new NetworkErrorException("Http return " + statusCode);
		}
		data.setContent(new String(readHttpResponse(response)));
		return data;
	}

	private static HttpClient getHttpClient() {
		try {

			HttpParams params = new BasicHttpParams();

			HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

			SchemeRegistry registry = new SchemeRegistry();

			registry.register(new Scheme(SCHEME_HTTP, PlainSocketFactory
					.getSocketFactory(), 80));
			registry.register(new Scheme(SCHEME_HTTPS,
					new EasySSLSocketFactory(), 443));
			registry.register(new Scheme(SCHEME_HTTPS,
					new EasySSLSocketFactory(), 8443));

			ClientConnectionManager ccm = new ThreadSafeClientConnManager(
					params, registry);

			HttpConnectionParams.setConnectionTimeout(params,
					CONNECTION_TIMEOUT);
			HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);

			HttpClient client = new DefaultHttpClient(ccm, params);
			return client;
		} catch (Exception e) {
			return new DefaultHttpClient();
		}
	}

	/**
	 * 读取 数据
	 * 
	 * @param response
	 * @return
	 * @throws IOException
	 * @throws IllegalStateException
	 */
	@SuppressLint("DefaultLocale")
	private static byte[] readHttpResponse(HttpResponse response)
			throws IllegalStateException, IOException {
		byte[] data = null;
		HttpEntity entity = response.getEntity();
		InputStream inputStream = entity.getContent();
		ByteArrayOutputStream content = new ByteArrayOutputStream();

		Header header = response.getFirstHeader("Content-Encoding");
		String headerStr = header != null ? header.getValue().toLowerCase()
				: "";
		if (header != null && headerStr.indexOf("gzip") > -1) {
			inputStream = new GZIPInputStream(inputStream);
		}

		int readBytes = 0;
		byte[] sBuffer = new byte[512];
		while ((readBytes = inputStream.read(sBuffer)) != -1) {
			content.write(sBuffer, 0, readBytes);
		}
		data = content.toByteArray();
		return data;
	}

	private static String bulildUrl(String url, WebParameters params) {
		if (params == null) {
			return url;
		}
		// has ?
		if (url.indexOf("?") > -1) {
			url += (url.indexOf("&") > -1 ? "" : "&") + params.toString();
		} else {
			url += "?" + params.toString();
		}
		return url;
	}
}
